2014年5月26日, 由Alexander Skutin撰写; 由Max Shirshin于2014年6月30日翻译

今天我想关注网页呈现的主题,以及为什么它在网页开发中很重要。有很多文章可用于涵盖这个主题,但是信息是分散的,以某种方式分散。例如,为了包围我的头脑,我不得不学习很多来源。这就是为什么我决定写这篇文章。我相信这篇文章对于初学者以及想要刷新和构建他们已经知道的更多高级开发人员将是有用的。

当页面布局被定义时,渲染必须从一开始就进行优化,因为样式和脚本在页面呈现中起关键作用。专业人士必须了解某些技巧以避免性能问题。

本文不详细研究内部浏览器的机制,而是提供一些常见的原则。不同的浏览器引擎的工作方式不同,这将使浏览器特定的研究变得更加复杂。

浏览器如何呈现网页
我们从绘制页面时浏览浏览器操作的概述开始:

1.DOM(文档对象模型)由从服务器接收的HTML形成。
2.样式被加载和解析,形成了CSSOM(CSS对象模型)。
3.在DOM和CSSOM之上,创建一个渲染树,它是一组要渲染的对象(Webkit调用那些“渲染器”或“渲染对象”,而在Gecko中它是一个“框架”)。渲染树反映除了不可见元素之外的DOM结构(如<head>标签或已display:none;设置的元素)。每个文本字符串在渲染树中作为单独的渲染器表示。每个渲染对象都包含其对应的DOM对象(或文本块)加上计算的样式。换句话说,渲染树描述了DOM的视觉表示。
对于每个渲染树元素,计算其坐标,称为“布局”。浏览器使用一个流程方法,只需要一次传递来布局所有元素(表需要多个遍)。
最后,这实际上显示在浏览器窗口中,一个称为“绘画”的过程。
当用户与页面进行交互或脚本进行修改时,必须重复上述某些操作,因为底层页面结构发生变化。

重印
当改变不影响页面上的元素的位置元素的样式(例如background-color,border-color,visibility),浏览器只是应用了新样式再次重绘元素(即意味着“重画”或“restyle”正在发生的事情)。

回流
当更改影响文档内容或结构或元素位置时,会发生回流(或重新传输)。这些更改通常是由以下机制触发的:

DOM操作(元素添加,删除,更改或更改元素顺序);
内容更改,包括表单字段中的文本更改;
1计算或改变CSS属性;
2添加或删除样式表;
3改变“类”属性;
4浏览器窗口操纵(调整大小,滚动);
5伪类激活(:hover)。
浏览器如何优化渲染
浏览器正在尽最大努力将重绘/回流限制到仅覆盖已更改元素的区域。例如,绝对/固定定位元素中的大小变化仅影响元素本身及其后代,而静态定位元素中的相似变化会触发所有后续元素的回流。

另一种优化技术是在运行JavaScript代码时,浏览器会缓存这些更改,并在代码运行后将其应用于单次传递。例如,这段代码只会触发一个回流和重绘:

var $body = $('body');
$body.css('padding', '1px'); // reflow, repaint
$body.css('color', 'red'); // repaint
$body.css('margin', '2px'); // reflow, repaint
// only 1 reflow and repaint will actually happen
但是,如上所述,访问元素属性会触发强制回流。如果我们添加一个额外的行,将元素属性读入上一个块,则会发生这种情况:

var $body = $('body');
$body.css('padding', '1px');
$body.css('padding'); // reading a property, a forced reflow
$body.css('color', 'red');
$body.css('margin', '2px');

因此,我们得到2个回流而不是一个回流。因此,您应该将读取元素属性组合在一起以优化性能(请参阅 JSBin上的更详细示例)。

有时您必须触发强制回流。示例:我们必须将相同的属性(例如“margin-left”)应用于同一个元素两次。最初,它应该被设置为100px没有动画,然后它必须是动画与transition一个值50px。您现在可以在JSBin上学习这个例子,但是我将在这里进行更详细的描述。

我们首先创建一个具有转换的CSS类:

.has-transition {
-webkit-transition: margin-left 1s ease-out;

  -moz-transition: margin-left 1s ease-out;
    -o-transition: margin-left 1s ease-out;
       transition: margin-left 1s ease-out;

}
然后继续执行:

// our element that has a "has-transition" class by default
var $targetElem = $('#targetElemId');

// remove the transition class
$targetElem.removeClass('has-transition');

// change the property expecting the transition to be off, as the class is not there
// anymore
$targetElem.css('margin-left', 100);

// put the transition class back
$targetElem.addClass('has-transition');

// change the property
$targetElem.css('margin-left', 50);
然而,这种实现不能像预期的那样工作。这些更改被缓存并应用于代码块的末尾。我们需要的是强制回流,我们可以通过进行以下更改来实现:

// remove the transition class
$(this).removeClass('has-transition');

// change the property
$(this).css('margin-left', 100);

// trigger a forced reflow, so that changes in a class/property get applied immediately
$(this)[0].offsetHeight; // an example, other properties would work, too

// put the transition class back
$(this).addClass('has-transition');

// change the property
$(this).css('margin-left', 50);
现在这样按预期工作。

优化实用建议
总结可用信息,我可以推荐以下内容:

创建有效的HTML和CSS,不要忘记指定文档编码。样式应包含在<head>中,附加到<body>标签末尾的脚本。
尝试简化和优化CSS选择器(这种优化几乎被大多数使用CSS预处理器的开发人员普遍忽略)。保持嵌套水平至少。这是CSS选择器根据其性能(从最快的)开始排名的方式:
识别者: #id
类: .class
标签: div
相邻的兄弟选择器: a + i
家长选择器 ul > li
通用选择器 *
属性选择器 input[type="text"]
伪类和pseudoelements:a:hover 你应该记住,浏览器从右到左的处理选择,这就是为什么最右边的选择应该是最快的国家之一-要么#id或.class:
div * {...} // bad
.list li {...} // bad
.list-item {...} // good

list .list-item {...} // good

在脚本中,尽量减少DOM操作。缓存所有内容,包括属性和对象(如果要重复使用)。执行复杂操作时,最好使用“离线”元素(“离线”元素是从DOM断开并仅存储在内存中),然后将其附加到DOM。
如果您使用jQuery选择元素,请遵循jQuery选择器最佳做法。
要更改元素的样式,修改“类”属性是最有效的方式之一。您执行此更改的DOM树越深,越好(也是因为这有助于将逻辑与演示分离)。
动画只有绝对/固定的元素,如果可以的话。
在:hover滚动时禁用复杂动画是一个好主意(例如,通过向<body>添加一个额外的“no-hover”类)。阅读有关此主题的文章。
有关更详细的概述,请查看这些文章:

浏览器的工作原理
渲染:重绘,回流/重新传输,修复
我希望你能发现这篇文章有用!

2014年6月30日
[RU] РендерингWEB-страницы:чтообэтомдолжензнать前端разработчик
亚历山大Skutin
现场: http://skutin.ru/
Max Shirshin
GitHub: ingdir
推特: @ingdir
Facebook 推特 Google+
如果您发现有错误,请随时在GitHub上进行 编辑。
评论由Disqus提供支持


YuanXingxing66
2 声望0 粉丝